// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CXFormsElement.
//   $Id$

// XFormsElement.cpp: implementation of the CXFormsElement class.
//
//////////////////////////////////////////////////////////////////////



#include "stdafx.h"
#include <new>
#include "XForms.h"
#include "XFormsApp.h"

#include "XFormsElement.h"
#include "FormControl.h"
#include "OnLoadHandler.h"
#include "Model.h"
#include "datetime.h"
#include "input.h"
#include "output.h"
#include "secret.h"
#include "textarea.h"
#include "ImplGroup.h"
#include "FP_Definitions.h"
#include "FP_ReturnCodes.h"
#include "eventmsg.h"
#include "TabletPCUtil.h"
#include "MSHTML-Events.h"
#include "dispex.h"
#include "tags.h"
#include <Common/ResourceHandling/msw/loadHTMLResource.h>
#include "ActivateBodyElement.h"

//#include "ExecuteDispatch.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define HIGHLIGHTED_COLOUR_ON L"yellow"
#define HIGHLIGHTED_COLOUR_OFF L"transparent"

extern CXFormsApp theApp;

#include "events.h"
#include "MSHTMLDomPurger.h"

const CString cg_sInput = _T("input");
const CString cg_sTextarea = _T("textarea");
const CString cg_sSecret = _T("secret");

//////////////////////////////////////////////////////////////////////
// CXFormsElement Class
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXFormsElement::CXFormsElement()
:m_dwCookie(0)
,m_pXForms(NULL)
,m_guidEvents(DIID_HTMLElementEvents)
,m_lGUIStringID(0)
,m_bElementProcessed(false)
,m_bDocumentReady(false)
,m_bEnclosingBindingResolved(false)
//temp, allows side-by-side development of external controls.
,m_bNewMethod(false)
,m_bUpdatePending(true)
,m_didPutFocus(0)
,m_didPutValue(0)
,m_didPutStyle(0)
,m_didExecCommand(0)
,m_dispidActivate(0)
,m_pFormControl(0)
,m_pImpl(0)
,m_bEnabled(true)
,m_bReadonly(false)
,m_bValid(true)
,m_bRequired(false)
,m_bFocus(false)
,m_bVisited(false)
,m_bPuttingValue(false)
,m_lFormKey(0)
,m_bIsButton(false)
,m_bBindingToTarget(false)
,m_sControlName(0)
,m_bElementRendered(false)
,m_bXFELEventsAttached(false)
{
	#ifdef COUNT_CXFORMSELEMENT
	++sm_Count;
#endif
}

#ifdef COUNT_CXFORMSELEMENT
unsigned long CXFormsElement::sm_Count = 0;
#endif

CXFormsElement::~CXFormsElement()
{

	if(m_pImpl)
	{
		//If this is being destroyed, then we don't 
		//	want release to be called by the Back-end destructor
		m_pImpl->m_pXFormsElement = 0;
		m_pImpl->RequestDeleteAtEarliestConvenience();
		m_pImpl = 0;
		m_pFormControl = 0;
		
	}
	CComPtr<IHTMLDocument> pDoc;
	GetViewlinkDoc(&pDoc);

	if(pDoc)
	{
		addXblParentNode(pDoc, 0);

		IUnknown * pUnk = 0;
		pDoc->QueryInterface(IID_IUnknown,(void**)&pUnk);
		ULONG l = pUnk->Release();
		if(l)
		{
			if(m_pXForms)
			{
				m_pXForms->DeregisterControlContainer(pUnk);
			}
			l = pUnk->Release();
			::safeRelease(pUnk);
		}
/*
		CMSHTMLDOMPurger * pPurger = CMSHTMLDOMPurger::GetPurger();
		if(pPurger)
			pPurger->Purge(pDoc);
*/	}
	
	m_lstEventNames.clear();
	detachEvents();

	CComPtr<IHTMLElement> pMaster;
	GetMasterElement(&pMaster);
	if(pMaster)
	{
/*		CMSHTMLDOMPurger * pPurger = CMSHTMLDOMPurger::GetPurger();
		if(pPurger)
			pPurger->Purge(pMaster);
*/	}
	if(m_pXForms)
		m_pXForms->RemoveDeletionListener(this);

	#ifdef COUNT_CXFORMSELEMENT
		--sm_Count;
	#endif

}


HRESULT CXFormsElement::StopAddingControlsAndInitialize()
{
	HRESULT hr = S_FALSE;
	//if this is the last to be called
	if(m_pXForms->StopAddingControls())
	{
		//StopAdding could delete the attached control.
		//	if this it does get deleted, that means that this
		//	is an erroneous control, don't continue.

		if(m_pImpl)
		{
			// Only call onload if this element is an outermost
			if(!m_pImpl->m_pParentImpl)
			{
				hr =  m_pXForms->onload();
			}				
		}
	}
	//In the case that this control is being inserted into an already initialised
	//	element, call initialize.
	if(m_pImpl->m_pParentImpl && static_cast<CFormControl*>(m_pImpl->m_pParentImpl)->m_bInitializeDone)
	{
		hr = m_pImpl->initialize(false);
	}
	return hr;
}

HRESULT CXFormsElement::changeUI()
{
	return S_FALSE;
}

HRESULT CXFormsElement::DocumentReady()
{
	HRESULT hr = S_FALSE;
	//set the documentReady flag to true.
	//	This means that if ElementReady is 
	//	running concurrently, it can fire
	//	documentReady when it has finished.
	m_bDocumentReady = true;
	//Only call onload if the this->m_bElementProcessed
	//	has been set to true.  In a larger document, 
	//	ElementReady and DocumentReady may be called at
	//	almost the same time for some elements.
	//	Do not call this for elements which have parentImpl,
	//	As they may receive documentReady before the parentImpl.
	if(
		//only proceed if ElementReady has completed.
		//	if this function is called during the execution
		//	of elementReady, elementReady will call this again
		//	when ready.
		m_bElementProcessed  
		//ensure that there is a form object to call the 
		//	onload method on.
		&& m_pXForms 
	)
	{
		if(m_pXForms->canContinue())
		{
			if(m_pImpl)
			{
				if(!m_pImpl->m_pParentImpl)
				{
					hr = StopAddingControlsAndInitialize();
				}
				else
				{
					CFormControl * pParentImplAsFormControl = dynamic_cast<CFormControl*>(m_pImpl->m_pParentImpl);
					if(pParentImplAsFormControl && pParentImplAsFormControl->m_bInitializeDone)
					{
						hr = StopAddingControlsAndInitialize();
					}
					else
					{
						CImplGroup * pImplAsGroup = dynamic_cast<CImplGroup*>(m_pImpl);
						if(pImplAsGroup && pImplAsGroup->m_bIsRepeatIteration)
						{
							hr = StopAddingControlsAndInitialize();
						}
					}

				}
			}
/*
//The above set of if/else business is to improve clarity and performance by removing one or two dynamic casts.
//	It should be the same as the single if, below.
			if
			(
				m_pImpl 
				&&
				(
					!m_pImpl->m_pParentImpl
					||
					(
						dynamic_cast<CFormControl*>(m_pImpl->m_pParentImpl)
						&&
						dynamic_cast<CFormControl*>(m_pImpl->m_pParentImpl)->m_bInitializeDone
					)
					||
					(
						dynamic_cast<CImplGroup*>(m_pImpl)
						&&
						dynamic_cast<CImplGroup*>(m_pImpl)->m_bIsRepeatIteration
					)
				)
			)
			{
				hr = StopAddingControlsAndInitialize();
			}
*/
			// Update the css once the behaviour recieves DocumentReady
			// this is due to problems with controls within repeats crashing 
			// on the call to get_XXX on the currentStyle object.
			// Fixes Bug 217
			updateAppearance();

			// HACK: Bug 499.
			// For a reason that I have not entirely been able to ascertain, xf:select and
			// xf:select1 controls in the last iteration of a repeat are not being correctly
			// initialised on loading a form. It appears that initialize() gets called on
			// these controls before any items are added to them. This leads to the controls
			// being marked as out-of-range and the wrong value getting displayed in the
			// rendered control when the form is loaded, despite the fact that the controls
			// are bound to the correct instance data. The following hack ensures that we
			// call initialize() again for any controls that are out-of-range. Ultimately,
			// it is important to understand the root cause of this problem and address
			// that, rather than merely treat the symptom as we do here.
			if(m_pImpl && (!m_pFormControl || (m_pFormControl && m_pFormControl->InRange() == false)))
				m_pImpl->initialize(true);

			m_pXForms->showNewOrphans();
			if(m_pXForms->m_XFConfig.ControlBranding())
				InsertBranding();
		}
		else
		{
			hr = E_FAIL;
		}
	}
	return hr; 
}

HRESULT CXFormsElement::resetScriptDispID() {
	m_didPutValue = m_didPutFocus = m_didPutStyle = m_didExecCommand = m_dispidActivate = 0;
	return S_OK;
}

HRESULT CXFormsElement::ElementReady()
{

	HRESULT hr = S_OK;
	CComPtr<IDispatch> pDisp;
	CComPtr<IHTMLElement> pMaster;
	GetMasterElement(&pMaster);
	AddRef();
	if(pMaster)
	{
		hr = pMaster->get_document(&pDisp);
		if(m_pXForms && SUCCEEDED(hr) && pDisp)
		{
			m_pXForms->StartAddingControls();

			//XForms has now been initialised, either in this call
			//	or a previous one.
	

			//IE will continue to hand events to us even if we have experienced
			//	a fatal exception, only act on those events if no fatal exception
			//	has already been reported
			if(m_pXForms->canContinue())
			{
				m_pXForms->onElementEncountered();
				


				if(m_pImpl)
				{
					// Create the correct parse object
					//	The implementation object is created
					//	in intBehavior
					m_pImpl->m_pXForms = m_pXForms;
					CXFormsParse* pParse = CreateParse(m_pImpl);
					//get the parentImpl member, must be done before parse,
					//	as child inits use parent->m_pParentImpl

					hr = ResolveEnclosingBinding(pMaster,&m_pImpl->m_pParentImpl);

					/*
					* Parse the incoming element into the implementation
					*/

					if (pParse)
					{
						pMaster.Release();
						GetMasterElement(&pMaster);
						if(pMaster)
						{
							hr = pParse->Parse(pMaster, m_pImpl);
							delete pParse;
							RegisterFindBehaviourAcceptance();

							if (S_OK == hr)
							{
								
								m_pImpl->setModel(0);
								if(m_pXForms && m_pImpl)
								{
									hr = m_pImpl->init(NULL);
									if (SUCCEEDED(hr) || E_ABORT == hr)
									{

										//create the path to the control fragment
										//if(m_bNewMethod)
											//createPath();
										render();
										//It is plausible that, due to threading issues, a form could be closed 
										//	during render.  Check that it still exists before continuing.
										if(m_pXForms && m_pImpl)
										{
											m_bElementRendered = true;
											if(m_pXForms->m_bDocumentInitialised)
												m_pImpl->initialize(false);
										}
										else
										{
											m_bDocumentReady =false;
											hr = E_FAIL;
										}
									}
									else
									{
										m_pXForms->reportError();
									}
								}
								else
								{

									//It is plausible that, due to threading issues, a form could be closed 
									//	during some annoying point in this function.
									hr = E_FAIL;
								}
							}
							else
							{
								m_pXForms->reportError(hr);
							}
						}
					}
					if(m_pXForms)
						m_pXForms->onElementComplete();
				}
				else
				{
					hr = E_FAIL;
				}
			}
			else
			{
				if(m_pImpl)
				{
					m_pImpl->m_pXForms = 0;
					m_pImpl->RequestDeleteAtEarliestConvenience();
				}
				m_pImpl = 0;
				m_pFormControl = 0;
				hr = E_FAIL;
			}
		}
	}
	//I have finished processing this element, so if DocumentReady comes along later, 
	//	it can send the onload command to the XFormsElement.
	m_bElementProcessed = true;
	
	if(m_bDocumentReady)
	{
		//If documentReady was called while this function was executing,
		//	it would have done nothing.  call it again.  This is like a 
		//	mutex or something.
		hr = DocumentReady();
	}

	Release();
	return hr;
}

HRESULT CXFormsElement::processNavIndex(IHTMLElement * pMaster) {
	// Set the Master HTML Element tabIndex to the navIndex of the Xforms control
	// so that the correct tabbing order takes place. 
	BSTR sStr = 0;
	// Get the value of the NavIndex attribute of the control
	if(m_pFormControl)
		m_pFormControl->getNavIndex(&sStr);
    
	if(sStr)
	{
		short iNavIndex = _wtoi(sStr);
		// Set the HTML attribute tabIndex to the value of the 
		// xforms navIndex.
		CComQIPtr<IHTMLElement2> pMaster2 = pMaster;
		pMaster2->put_tabIndex(iNavIndex);
	}

	::SysFreeString(sStr);

	return S_OK;
}

HRESULT CXFormsElement::processAccessKey(IHTMLElement * pMaster) {
	// After the rendering has taken place now need to set the value of the 
	// accesskey to the UIScripts control. This need to be after the render 
	// otherwise there is no script to send the data.
	BSTR sStr = 0;
	// Get the value of the accesskey
	if(m_pFormControl)
		m_pFormControl->getAccessKey(&sStr);

	if(sStr)
	{
		// blank the accesskey for the master element so that it
		// doesn't take focus....
		CComQIPtr<IHTMLElement2> pMaster2 = pMaster;
		BSTR sBlankStr = SysAllocString(L"");
		pMaster2->put_accessKey(sBlankStr);
		SysFreeString(sBlankStr);
		sBlankStr = 0;
		// Write the accesskey to the UIScrips Control so that it takes the
		// focus of the key press.
		put_accesskey(sStr);
		::SysFreeString(sStr);
	}

	return S_OK;
}
#define XFORMSELEMENT_RENDER_MUTEX 1
#undef XFORMSELEMENT_RENDER_MUTEX // Temporary disabling for release before readiness
#ifdef XFORMSELEMENT_RENDER_MUTEX
HANDLE g_hRenderMutex = 0;
#include "waitwithmessageloop.h"
#endif

CComQIPtr<IHTMLDOMNode> g_PrototypeBlankDoc;

HRESULT GetBlankDocument(CComPtr<IHTMLDocument4> & pDoc4, CComQIPtr<IHTMLDocument2> & pDoc)
{
	//cloning about:blank does not work!!!
	//	document.write() returns E_FAIL later.
	//	what a heap of crap!
	HRESULT hr = E_PENDING;
	if(!g_PrototypeBlankDoc)
	{
		//CComPtr<IHTMLDocument2> pDoc2;
		hr = pDoc4->createDocumentFromUrl(g_bsBlankURL, 0, &pDoc);
//		g_PrototypeBlankDoc	= pDoc2;
	}
	if(g_PrototypeBlankDoc)
	{
		CComPtr<IHTMLDOMNode> pNode;
		hr = g_PrototypeBlankDoc->cloneNode(VARIANT_TRUE,&pNode);
		pDoc = pNode;
		if(!pDoc)
			hr = E_NOINTERFACE;
	}
	return hr;
}

HRESULT CXFormsElement::render()
{
#if DEBUG_LEVEL & DEBUG_MONITOR_EVENTS
	AtlTrace("'formsplayer.dll':Rendering - %X,id=%S...\n",this,m_pImpl->m_sId);
#endif

	HRESULT hr = S_OK;
	CComPtr<IHTMLElement> pMaster;

	GetMasterElement(&pMaster);
	if (pMaster)
	{

		/*
		 * Get the containing document to use as a factory ...
		 */
		CComPtr<IHTMLDocument4> pDoc4;
		m_pXForms->GetHostDocument(&pDoc4);
		
		if (pDoc4)
		{
			processNavIndex(pMaster);
			
			/*
			 * ... to create an empty IHTMLDocument2
			 */
			
			CComQIPtr<IHTMLDocument2> pDoc2;
			GetBlankDocument(pDoc4,pDoc2);
			/*
			 * Under some circumstances we can get a successful completion,
			 * but no document, so check the pointer
			 */

			if(SUCCEEDED(hr) && pDoc2)
			{

				#ifdef XFORMSELEMENT_RENDER_MUTEX
				if(!g_hRenderMutex)
					g_hRenderMutex = CreateMutex(0, TRUE, _T("XPORT_FP_mutex_Render"));
				else if(g_hRenderMutex)
					xport::WaitWithMessageLoop(g_hRenderMutex);
				#endif
				if(m_pXForms)
					m_pXForms->RegisterControlContainer(m_pImpl, pDoc2);
				//	Get the IHTMLDocument version of our document
				//	and put it in the viewLink.  Set the Viewlink
				//	properties.
				 // Now we can fill the document with the HTML we need
				CComQIPtr<IHTMLDocument> pViewLinkDoc = pDoc2;
				hr = SetViewlinkProperties(pViewLinkDoc,VARIANT_TRUE,VARIANT_TRUE,VARIANT_FALSE);

/*
				MSG msg;
				// force msg-q to be created just in case, NOP otherwise
				PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE | PM_NOYIELD);
				// msg to myself to do work
				
				DWORD dwThread = GetCurrentThreadId();
				LPARAM p = reinterpret_cast<LPARAM>(this);
				PostThreadMessage(dwThread,WM_USER+25, 0, p);
				bool bWritten = false;
				// msg-pump
				while (PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE| PM_NOYIELD))
				{

					if (msg.message == WM_USER+25 && msg.hwnd == NULL && msg.lParam == p)
					{
						
*/						WriteUI(pDoc2);
						ASSERT(m_pXForms);
						if(m_pXForms)
							m_pXForms->AddScriptLibrariesToViewlinkDocument(pDoc2);
						//disabled - is almost certainly causing french crash
						
						hr = addHostDocumentProperty(pViewLinkDoc, pDoc4);
					//	hr = addXblParentNode(pViewLinkDoc, pMaster);
				//		bWritten = true;
/*					}
					else
					{
						TranslateMessage(&msg);
						DispatchMessage(&msg);
					}
				}
				if(!bWritten)
				{
					WriteUI(pDoc2);
					m_pXForms->AddScriptLibrariesToViewlinkDocument(pDoc2);
				}
*/				//keep a pointer to the "window".  This 
				//	will be the viewlink. rendering pane

				hr = pDoc2->get_parentWindow(&m_pControlWin);

				//Retrieve pointers to any important nodes,
				//control, main etc.
				CComQIPtr<IHTMLDocument3> pDoc3 = pDoc2;
			
				hr = getSignificantDescendentNodes(pDoc3);
			
				//	Finally attach any events to the control
				if (m_bNewMethod)
				{
					CComObject<CEventSink> *pSink;
					hr = CComObject<CEventSink>::CreateInstance(&pSink);
					if (SUCCEEDED(hr))
					{
						if(m_pFormControl) {
							pSink->m_pFormControl = m_pFormControl;
		                    pSink->m_pControl = this;
							pSink->m_bHandleThroughHandleEvent = true;
							m_pEventSink = pSink;
							//Create the appropriate list of
							//	events for this object.
							populateEvents();
							//Assign the event handler to the events on the UI.
							attachEvents(pDoc3);
						}
					}
				}
				else if (m_pControl)
				{
					CComObject<CEventSink> *pSink;
					hr = CComObject<CEventSink>::CreateInstance(&pSink);
					if (SUCCEEDED(hr))
					{
						if(m_pFormControl) {
							pSink->m_pFormControl = m_pFormControl;
							pSink->m_pControl = this;
							m_pEventSink = pSink;
							hr = attachEvents();
						}
					}
					//Attach the appropriate event handlers.
				}//if ( we have a 'control' part

                processAccessKey(pMaster);
				
				#ifdef XFORMSELEMENT_RENDER_MUTEX
					ReleaseMutex(g_hRenderMutex);
				#endif
				/*
				 *	Then apply the style of the master to the control
				 *	that we have created.
				 */
				//moved to updateAppearance
//				if (m_pControl || m_pControlWin)
//					hr = copyStyle();
			}//if ( the document to pipe into was created )
		}//if ( created document factory )
	}//if ( the control has a master element )

#if DEBUG_LEVEL & DEBUG_MONITOR_EVENTS
	AtlTrace("'formsplayer.dll': Rendering done %X,id=%S\n",this,m_pImpl->m_sId);
#endif 
	return hr;
} 


CImpl* CXFormsElement::CreateImpl()
{
	CImpl* pFormControl = (CImpl*) new CFormControl(this);

	return pFormControl;
}

CXFormsParse* CXFormsElement::CreateParse(CImpl* pImpl)
{
	CXFormsParse* pParse = new CXFormsParse(pImpl);

	return pParse;
}

/*
 * The following routines are defaults, so that if a derived class doesn't need them, it need not
 * supply them
 */


//TODO implement for Variant

 HRESULT CXFormsElement::get_value(BSTR* psValue)
{
	HRESULT hr = S_OK;
	if(m_bNewMethod && m_pControlElement)
	{
		CComVariant vt;
		BSTR bs = ::SysAllocString(L"value");
		m_pControlElement->getAttribute(bs,2,&vt);
		::SysFreeString(bs);
		if(vt.vt == VT_BSTR)
		{
			*psValue = ::SysAllocString(vt.bstrVal);
		}
		else if(vt.vt == VT_I4)
		{
			//convert the number to an appropriate string.
			hr = S_FALSE;
		}
		else
		{
			*psValue = 0;
		}
	}

	return hr;
}

 HRESULT CXFormsElement::get_valuechanging(BSTR* psValue)
{
	HRESULT hr = S_OK;
	if(m_bNewMethod && m_pControlElement)
	{
		CComVariant vt;
		BSTR bs = ::SysAllocString(L"valuechanging");
		m_pControlElement->getAttribute(bs,2,&vt);
		::SysFreeString(bs);
		if(vt.vt == VT_BSTR)
		{
			*psValue = ::SysAllocString(vt.bstrVal);
		}
		else if(vt.vt == VT_I4)
		{
			//convert the number to an appropriate string.
			hr = S_FALSE;
		}
		else
		{
			*psValue = 0;
		}
	}

	return hr;
}


HRESULT CXFormsElement::put_value(BSTR bsValue)
{
	HRESULT hr = S_FALSE;
	if(m_bNewMethod && m_pControlWin)
	{
		if(m_sCurrentValue == "" || m_sCurrentValue != bsValue) {
			// If its changed then keep a copy.
			m_sCurrentValue = bsValue;
			CComVariant vtArg(bsValue);
			DISPPARAMS params = {&vtArg,0,1,0};
			CComVariant vt;
			m_bPuttingValue = true;
			const CComBSTR sFuncName(L"put_value");
			hr = UIExecFunction(sFuncName,&params,&vt,m_didPutValue);
			m_bPuttingValue = false;
		}
	}
	return hr;
}

HRESULT CXFormsElement::put_navindex(BSTR bsValue)
{
	HRESULT hr = S_FALSE;
	if(m_bNewMethod && m_pControlWin)
	{
		CComVariant vtArg(bsValue);
		DISPPARAMS params = {&vtArg,0,1,0};
		CComVariant vt;
		DISPID did = 0;
		const CComBSTR sFuncName(L"put_navindex");
		hr = UIExecFunction(sFuncName,&params,&vt,did);

	}
	return hr;
}

HRESULT CXFormsElement::put_accesskey(BSTR bsValue)
{
	HRESULT hr = S_FALSE;
	if(m_bNewMethod && m_pControlWin)
	{
		CComVariant vtArg(bsValue);
		DISPPARAMS params = {&vtArg,0,1,0};
		CComVariant vt;
		DISPID did = 0;
		const CComBSTR sFuncName(L"put_accesskey");
		hr = UIExecFunction(sFuncName,&params,&vt,did);

	}
	return hr;
}

HRESULT CXFormsElement::attachEvents(IHTMLDocument3 * pDoc3)
{
	HRESULT hr = S_OK;
	//m_bXFELEventsAttached prevents double calling of attachevents and detachevents.
	if(m_bNewMethod && m_pEventSink && !m_bXFELEventsAttached)
	{
		if(pDoc3)
		{
			VARIANT_BOOL b = true;
			CComBSTRPtrList::iterator i = m_lstEventNames.begin();
			while(i != m_lstEventNames.end() &&  SUCCEEDED(hr) && b)
			{
				hr = pDoc3->attachEvent((*i)->m_str,m_pEventSink,&b);				
				//TODO: add error handling to allow us to know which
				//		event failed to attach;
				ASSERT(SUCCEEDED(hr) && b);
				++i;
			}

			CComPtr<IHTMLElement> pMaster;
			GetMasterElement(&pMaster);
			if(pMaster)
			{
				CComQIPtr<IHTMLElement2> pMaster2 = pMaster;
				pMaster2->attachEvent(EVENT_onfocus,m_pEventSink,&b);
				//pMaster2->attachEvent(EVENT_onclick,m_pEventSink,&b);
				pMaster2->attachEvent(EVENT_onfocusout,m_pEventSink,&b);

				// These events are not fired on the document so we need to 
				// handle them on the master element itself. The events have been 
				// added to allow document which are within framesets to handle
				// the Cut, Paste and Copy. Bug 197
				pMaster2->attachEvent(EVENT_onbeforecopy,m_pEventSink,&b);
				pMaster2->attachEvent(EVENT_onbeforecut,m_pEventSink,&b);

				pMaster2->attachEvent(EVENT_onbeforepaste,m_pEventSink,&b);
			}
		}
		m_bXFELEventsAttached = true;
	}
	return hr;
}


HRESULT CXFormsElement::attachEvents(IHTMLElement2 * /* pEl2*/ /*, CStringList * pEventList = 0*/)
{
	HRESULT hr = S_OK;
	return hr;
}


HRESULT CXFormsElement::RegisterCustomEvents(const CComBSTR * const aEventNames[],long lCount, long aCookies[])
{
	HRESULT hr = S_OK;
	
	for(int i = 0;i < lCount;++i)
	{
		hr = RegisterEvent(*aEventNames[i],&aCookies[i]);
	}

	return hr;
}


void CXFormsElement::RegisterFindBehaviourAcceptance()
{
	if(m_pXForms) {
		CComPtr<IHTMLElement> pEl;
		GetMasterElement(&pEl);
		if(pEl) {
			//pEl->get_innerHTML(&m_pFormControl->m_sContent);
			//pEl->put_innerHTML(0);
			CComPtr<IFactory> pFactory;
			m_pXForms->GetElementFactory(&IID_IFactory, reinterpret_cast<void**>(&pFactory));
			if(pFactory)
				pFactory->UnsetFindBehaviourResponseWithin(pEl);
		}
	}
}

HRESULT CXFormsElement::InitBehaviour()
{
	RegisterName(g_bsAttrXFormsElementController);

	//TODO: could move to put_formKey;
	if(m_lFormKey)
		theApp.FormsManager().GetForm(m_lFormKey,&m_pXForms);

	if(m_pXForms)
		m_pXForms->AddDeletionListener(this);

	m_pImpl = CreateImpl();
	m_pFormControl = dynamic_cast<CFormControl *> (m_pImpl);
	return RegisterCustomEvents(sm_asEventNames,CXFORMSELEMENT_EVENTS_LENGTH, m_alEventCookies);
}




HRESULT CXFormsElement::putDisabledValue()
{
	BSTR sBlank = ::SysAllocString(L"");
	HRESULT hr = put_value(sBlank);
	::SysFreeString(sBlank);
	return hr;
}

const CComBSTR bsClassHover(" pc-hover");
const CComBSTR bsClassNoHover("");

void CXFormsElement::StyleHoverishly()
{
	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	if(pEl)
	{
		SetClassProperty(pEl,bsClassHover,bsClassNoHover);
		pEl.Release();
	}
	CComPtr<IHTMLDocument> pDoc;
	GetViewlinkDoc(&pDoc);
	CComQIPtr<IHTMLDocument2> pDoc2(pDoc);
	if(pDoc2)
	{
		pDoc2->get_body(&pEl);
		SetClassProperty(pEl,bsClassHover,bsClassNoHover);
	}
}

void CXFormsElement::StyleUnHoverishly()
{
	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	if(pEl)
	{
		SetClassProperty(pEl,bsClassNoHover,bsClassHover);
		pEl.Release();
	}
	CComPtr<IHTMLDocument> pDoc;
	GetViewlinkDoc(&pDoc);
	CComQIPtr<IHTMLDocument2> pDoc2(pDoc);
	if(pDoc2)
	{
		pDoc2->get_body(&pEl);
		SetClassProperty(pEl,bsClassNoHover,bsClassHover);
	}

}



HRESULT CXFormsElement::handleEvent(CComBSTR& sEventName, IHTMLEventObj2 * pEventObject,VARIANT * pvarResult)
{
	HRESULT hr = S_FALSE;
	if(m_pImpl) {
		if(sEventName == L"propertychange")
		{
			CComBSTR s;
			pEventObject->get_propertyName(&s);
			if(!m_bPuttingValue && s == "value")
			{
				// If enabled then update the instance
				// else blank the control as is not relavent
				CComBSTR sVal;
				get_value(&sVal);
				// Bug 964 - Update the cached value so that the object and the control 
				// have the same value.
				m_sCurrentValue = sVal;

				if(m_bEnabled)
				{
					if(m_pFormControl)
 						hr = m_pFormControl->putValue(sVal.m_str);
				}
				else
					hr = putDisabledValue();

			}
			else if(s == "valuechanging")
			{
				BOOL bIncremental = false;
				if(m_pFormControl) {
					m_pFormControl->getIncremental(&bIncremental);

					if(bIncremental) {
						CComBSTR sVal;
						get_valuechanging(&sVal);
						// Bug 964 - Update the cached value so that the object and the control 
						// have the same value.
						m_sCurrentValue = sVal;
						hr = m_pFormControl->putValue(sVal.m_str);
					}
				}
			}
	/*		else if(s="out-of-range")
			{
				//The UI is telling us that 
				//	it is unable to display
				//	the given value.
				m_pFormControl->onOutOfRange();
			}
	*/	}
		else if(sEventName == L"help")
		{
			if(m_pFormControl)
				hr = m_pFormControl->onhelp();

			CComQIPtr<IHTMLEventObj> pEvObj0 = pEventObject;
			
			//disable the default IE HTML event processing for this event.
			pvarResult->boolVal = FALSE;
			pEvObj0->put_cancelBubble(VARIANT_TRUE);
			pEvObj0->put_returnValue(CComVariant(false));
		}
		else if(sEventName == L"focusout")
		{
			if(xport::RunningOnTabletPC() && m_pXForms && m_sControlName && (cg_sInput == m_sControlName || cg_sTextarea == m_sControlName || cg_sSecret == m_sControlName))
				m_pXForms->HideTIP();

			VARIANT_BOOL vtB = VARIANT_FALSE;
			CComPtr<IHTMLElement> pToEl;
			pEventObject->get_toElement(&pToEl);
			if(pToEl)
			{
				CComPtr<IHTMLDocument> pDoc;
				GetViewlinkDoc(&pDoc);
				CComQIPtr<IHTMLDocument2> pDoc2 = pDoc;
				if(pDoc2)
				{
					CComPtr<IHTMLElement> pViewlinkBody;
					pDoc2->get_body(&pViewlinkBody);
					if(pViewlinkBody)
					{
						pViewlinkBody->contains(pToEl,&vtB);
					}
				}
				else
				{
					CComPtr<IHTMLElement> pMaster;
					GetMasterElement(&pMaster);
					if(pMaster)
					{
						pMaster->contains(pToEl,&vtB);
					}
				}
			}
			if(!vtB)
			{
				if(m_pFormControl)
					hr = m_pFormControl->onDOMFocusOut();
				showFocus(false);
			}

		}
		else if(sEventName == L"focus")
		{
			if(xport::RunningOnTabletPC() && m_pXForms && m_sControlName && (cg_sInput == m_sControlName || cg_sTextarea == m_sControlName || cg_sSecret == m_sControlName))
				m_pXForms->ShowTIP();

			//Find out whether focus is coming into this control, or whether some contained node is already focussed
			VARIANT_BOOL vtB = VARIANT_FALSE;
			CComPtr<IHTMLElement> pFromEl;
			pEventObject->get_fromElement(&pFromEl);
			if(pFromEl)
			{
				CComPtr<IHTMLDocument> pDoc;
				GetViewlinkDoc(&pDoc);
				CComQIPtr<IHTMLDocument2> pDoc2 = pDoc;
				if(pDoc2)
				{
					CComPtr<IHTMLElement> pViewlinkBody;
					pDoc2->get_body(&pViewlinkBody);
					if(pViewlinkBody)
					{
						pViewlinkBody->contains(pFromEl,&vtB);
					}
				}
			}

			if(m_pXForms)
			{
				CComPtr<IHTMLDocument2> pDoc2;
				m_pXForms->GetOwnerDocument(&pDoc2);
				CComQIPtr<IDisplayServices> pDispServ(pDoc2);
				if(pDispServ)
				{
					CComPtr<IHTMLCaret> pCaret;
					pDispServ->GetCaret(&pCaret);
					if(pCaret)
						pCaret->Hide();
				}
			}

			if(!vtB && m_pFormControl)
			{
				// The DOMFocusIn Event is now fired on the HTML focus event. The move 
				// is due to a problems with the cursor not moving with the focus when 
				// handled on the HTML focusin event.
				hr = m_pFormControl->onDOMFocusIn();
				if(m_bIsButton == true) {
					// Bug 567: Trigger not blurring focus from other controls.
					CComVariant vt;
					DISPPARAMS params = {0, 0, 0, 0};
					CComBSTR sFuncName(L"put_focus");
					hr = UIExecFunction(sFuncName, &params, &vt, m_didPutFocus);
				}
				showFocus(true);

			}
		}
		else if(sEventName == L"focusin")
		{
			// The DOMFocusIn Event is now fired on the HTML focus event. This is due to
			// the cursor not changing with the focus when handle on the focusin event.
			// 	hr = m_pFormControl->onDOMFocusIn();
		}
		else if(sEventName == L"keydown")
		{
			CComQIPtr<IHTMLEventObj> pEvObj0 = pEventObject;
			long lKey = 0;
			pEvObj0->get_keyCode(&lKey);
			
			if(m_pFormControl && lKey == 9) // if shift & tab
			{
				VARIANT_BOOL bShift;
				pEvObj0->get_shiftKey(&bShift);
				if(bShift)
					m_pFormControl->onprevious(); // fire previous event
				else
					m_pFormControl->onnext();
			}
			else if(m_pFormControl && lKey == 46)//delete
			{
				BSTR sCmdID = ::SysAllocString(L"Delete");
				onBeforeEventExecCommand(sCmdID);
				::SysFreeString(sCmdID);
			}
		}
		else if(sEventName == L"keyup")
		{
			CComQIPtr<IHTMLEventObj> pEvObj0 = pEventObject;
			long lKey = 0;
			pEvObj0->get_keyCode(&lKey);
		} 
		else if(sEventName == L"keypress")
		{
			CComQIPtr<IHTMLEventObj> pEv = pEventObject;
			if(pEv) {
				long nCode = 0;
				pEv->get_keyCode(&nCode);
				// Bug 674: Fire DOMActivate on <ENTER>.
				if(nCode == 13 && m_bIsButton == false && m_pFormControl) {
					CComVariant vResult;
					DISPPARAMS dp = {0, 0, 0, 0};
					UIExecFunction(CComBSTR(L"activate"), &dp, &vResult, m_dispidActivate);
					m_pFormControl->onactivate();
				}
			}
		}
		else if(sEventName == L"beforecopy")
		{
			// Handle the onbeforecopy event for viewlinks
			// in side framesets. Bug 197
			BSTR sCmdID = ::SysAllocString(L"Copy");
			onBeforeEventExecCommand(sCmdID);
			::SysFreeString(sCmdID);
		}
		else if(sEventName == L"beforecut")
		{
			// Handle the onbeforecut event for viewlinks
			// in side framesets. Bug 197
			BSTR sCmdID = ::SysAllocString(L"Cut");
			onBeforeEventExecCommand(sCmdID);
			::SysFreeString(sCmdID);
		}
		else if(sEventName == L"beforepaste")
		{
			// Handle the onbeforepaste event for viewlinks
			// in side framesets. Bug 197
			BSTR sCmdID = ::SysAllocString(L"Paste");
			onBeforeEventExecCommand(sCmdID);
			::SysFreeString(sCmdID);
		}
		else if(sEventName == L"mouseover")
		{
			//if(m_bFocus && xport::RunningOnTabletPC() && m_pXForms && (cg_sInput == m_sControlName || cg_sTextarea == m_sControlName || cg_sSecret == m_sControlName))
			//	m_pXForms->ShowPIP();

			StyleHoverishly();
		}
		else if(sEventName == L"mouseout")
		{
			//if(m_bFocus && xport::RunningOnTabletPC() && m_pXForms && (cg_sInput == m_sControlName || cg_sTextarea == m_sControlName || cg_sSecret == m_sControlName))
			//	m_pXForms->HidePIP();

			StyleUnHoverishly();
		}
		else
		{
			//Should not normally reach this point.
			::AtlTrace("'formsplayer.dll': unhandled event '%S' occured on control %X",sEventName,this);
		}
	}


	if(sEventName == L"keyup")
	{
		long lKey = 0;
		CComQIPtr<IHTMLEventObj> pEvObj0 = pEventObject;
		if(lKey == 13)
			int o = 0;
	}

	return hr;
}

/// <summary>Call the ExecCommand function within the UIScript only if 
/// the document is in a frameset passing the specified command. It is up to the 
/// UIScript developer to determine whether the command should be excuted.
/// <param name="sCmdID">Is the command to be sent to the ExecCommand. For example
/// copy, paste, cut</param>
/// <remarks>This function has been introduced due to a limitation of IE. The problem 
/// appears when viewlinks are contained within a frameset. Please see Bug 197.
/// </remarks>
/// </summary>
HRESULT CXFormsElement::onBeforeEventExecCommand(BSTR sCmdID) 
{
	// If there are frames then we need to handle the event ourselves.
	// This defiantly implies to the copy, cut, paste. As there is a problem 
	// with viewlinks that are contained in documents which has framesets. It 
	// appears that the document within viewlink can not be accessed, so we use the
	// UIExecFunction to send the command to the script.
	bool bInFrameset = false;
	HRESULT hr = m_pXForms->IsOperatingWithinFrameset(bInFrameset);
	
	if(bInFrameset && SUCCEEDED(hr))
	{
		// Get the viewlink for this element
		CComPtr<IHTMLDocument> pViewDoc;
		hr = GetViewlinkDoc(&pViewDoc);
		CComQIPtr<IHTMLDocument2> pViewDoc2 = pViewDoc;

		if(pViewDoc2 && SUCCEEDED(hr))
		{
			CComVariant vt;
			CComVariant vtCommand(sCmdID);
			VARIANTARG args[1];
			args[0] = vtCommand;
			DISPPARAMS params = {args,0,1,0};
			BSTR sFunc = ::SysAllocString(L"ExecCommand");
			hr = UIExecFunction(sFunc, &params, &vt, m_didExecCommand);
			::SysFreeString(sFunc);
		}
	}

	return hr;
}

HRESULT CXFormsElement::handleEvent(DISPID eventID,VARIANT* pvarResult,DISPPARAMS* pdispparams)
{

#if defined(USE_WAIT_CURSOR)
	HCURSOR hcWait, hcOld;
	if(m_bDocumentReady) {  // Bug 299
		hcWait = LoadCursor(0, IDC_WAIT);
		hcOld = SetCursor(hcWait);
	}
#endif
	HRESULT hr = S_FALSE;
	switch (eventID) 
	{
		case 0: // This means that the evenet was registered using attachEvent
				//	and will require us to examine the string name of the event 
				//	to find out what it is.
			if(pdispparams->rgvarg && pdispparams->rgvarg->vt == VT_DISPATCH)
			{
				CComQIPtr<IHTMLEventObj2> pEvObj = pdispparams->rgvarg->pdispVal;
				if(pEvObj) {
					CComBSTR s;
					pEvObj->get_type(&s);
					hr = handleEvent(s,pEvObj,pvarResult);
				}
			}
		break;
		case DISPID_HTMLELEMENTEVENTS_ONBLUR:
			if(m_pFormControl)
				hr = m_pFormControl->onDOMFocusOut();
			break;

		case DISPID_HTMLELEMENTEVENTS_ONFOCUS:
			if(m_pFormControl)
				hr = m_pFormControl->onfocus();
			break;
		case DISPID_HTMLELEMENTEVENTS_ONCLICK:
			if(m_pFormControl)
				hr = m_pFormControl->onactivate();
			break;

		case DISPID_HTMLINPUTTEXTELEMENTEVENTS_ONCHANGE:
			if(m_pFormControl)
				hr = m_pFormControl->onvaluechanged();
			break;

		case DISPID_HTMLELEMENTEVENTS_ONHELP:
			if(m_pFormControl)
				hr = m_pFormControl->onhelp();
			*pvarResult = _variant_t(false);//prevent IE help from appearing.
			break;
		default:
			break;
	}
#if defined(USE_WAIT_CURSOR)
	if(m_bDocumentReady)
		SetCursor(hcOld);
#endif
	return hr;
}

HRESULT CXFormsElement::formControlInitialize()
{
	//do nothing, this function will be overridden if 
	//there is any first-time processing to do.

	//This will include dataType dependent front ends.
	//such as a DT picker for input.

	return S_OK;
}

HRESULT CXFormsElement::detachEvents()
{
	HRESULT hr = S_FALSE;
	if(m_bNewMethod && m_bXFELEventsAttached)
	{
		CComPtr<IHTMLDocument> pDoc;
		hr = GetViewlinkDoc(&pDoc);

		if(SUCCEEDED(hr) && pDoc)
		{
			CComQIPtr<IHTMLDocument3> pDoc3 = pDoc;
			if(pDoc3) {
				CComBSTRPtrList::iterator i = m_lstEventNames.begin();
				while(i != m_lstEventNames.end() && SUCCEEDED(hr))
				{
					hr = pDoc3->detachEvent((*i)->m_str,m_pEventSink);
					//TODO: add error handling to allow us to know which
					//		event failed to detach;
					++i;
				}
			}
		}
		CComPtr<IHTMLElement> pMaster;
		GetMasterElement(&pMaster);
		if(pMaster)
		{

			CComQIPtr<IHTMLElement2> pMaster2 = pMaster;
			
			pMaster2->detachEvent(EVENT_onfocus,m_pEventSink);
			//pMaster2->detachEvent(EVENT_onclick,m_pEventSink);

			pMaster2->detachEvent(EVENT_onfocusout,m_pEventSink);

			// These events are not fired on the document so we need to 
			// handle them on the master element itself. The events have been 
			// added to allow document which are within framesets to handle
			// the Cut, Paste and Copy. Bug 197
			pMaster2->detachEvent(EVENT_onbeforecopy,m_pEventSink);
			
			pMaster2->detachEvent(EVENT_onbeforecut,m_pEventSink);

			pMaster2->detachEvent(EVENT_onbeforepaste,m_pEventSink);
		}
		m_bXFELEventsAttached = false;
	}
	else if (m_pControl && m_dwCookie && m_bXFELEventsAttached)
	{
		m_bXFELEventsAttached = false;
		hr =  AtlUnadvise(m_pControl, m_guidEvents,m_dwCookie);
		m_dwCookie = 0;
	}

	return hr;
}


HRESULT CXFormsElement::copyStyle()
{	
	//TODO: should copy the dimensions of the masterElement to the 
	//		body of the viewlink, then set min-width of the control
	//		to 100% if captionless, or calculate it based on the width
	//		of the caption.  If the caption has no sepcified width, 
	//		70 - 30?


	HRESULT hr = S_FALSE;
	if(m_pControl)
	{



		
/*
		//copy the class name to the appropriate new element.
		CComBSTR sClass;
		m_pMasterElement->get_className(&sClass);
		if(SUCCEEDED(hr))
		{
			if(m_pMain)
				hr = m_pMain->put_className(sClass);
			else if(m_pControl)
				hr = m_pControl->put_className(sClass);
		}
		if(SUCCEEDED(hr))
			hr = CBinaryBehavior::copyStyle(pEl,m_pControl);
*/
		CComPtr<IHTMLElement> pMaster;
		GetMasterElement(&pMaster);
		if(pMaster)
		{
			CComQIPtr<IHTMLElement2> pEl = pMaster;
			
			//make sure that setting an absolute 
			//	width does not cause a scrollbar 
			//	to appear.
			if(pEl)
			{
				CComBSTR s;
				CComPtr<IHTMLStyle> pStyle;
				hr = pMaster->get_style(&pStyle);
	
				CBinaryBehavior::getCurrentStyleAsText(pEl,&s);
				BSTR bs = ::SysAllocString(L"self");
				put_style(bs, s.m_str);
				::SysFreeString(bs);

				if(SUCCEEDED(hr)) {
					bs = ::SysAllocString(L"hidden");
					pStyle->put_overflow(bs);
					::SysFreeString(bs);
				}
			}
		}
	}
	return hr;
}





HRESULT CXFormsElement::getSignificantDescendentNodes(IHTMLDocument3 * pDoc3)
{
	HRESULT hr = S_OK;
	if(pDoc3)
	{
		if(m_bNewMethod)
		{ 
			if(!m_pControlElement)
				//new control element.
				hr = pDoc3->get_documentElement(&m_pControlElement);
			if(!m_pDisablee)
			{
				CComBSTR bs(L"control");
				CComPtr<IHTMLElement> pEl;
				hr = pDoc3->getElementById(bs, &pEl);
				m_pDisablee =pEl;
			}

		}
		else
		{
			CComBSTR bs(L"main");
			hr = pDoc3->getElementById(bs, &m_pMain);
			bs = L"control";
			hr = pDoc3->getElementById(bs, &m_pControl);
		}
	
	}
	return hr;
}



HRESULT CXFormsElement::highlight(bool bApply,bool bFocus)
{	
	//bool arg says whether to apply or retract the highlight.

	HRESULT hr = E_FAIL;
	if(m_pControl)
	{
		//Get the style property of the control
		CComPtr<IHTMLStyle> pStyle;
		hr = m_pControl->get_style(&pStyle);
		if(SUCCEEDED(hr))
		{
			//appropriately (de)highlight it.
			CComVariant v;
			v.vt = VT_BSTR;
			if(bApply)
			{
				v.bstrVal = ::SysAllocString(HIGHLIGHTED_COLOUR_ON);
				pStyle->put_backgroundColor(v);
			}
			else
			{
				v.bstrVal = ::SysAllocString(HIGHLIGHTED_COLOUR_OFF);
				pStyle->put_backgroundColor(v);
			}
			//::SysFreeString(v.bstrVal);
		}
	}
	return hr;
}

HRESULT CXFormsElement::getUI(CComBSTR &sHTML)
{
	HRESULT hr = S_FALSE;
	if(m_bNewMethod)
	{
		CString sResName = m_sControlName;
		sResName += ".xml";

		CComBSTR sResHTML;
		xport::loadHTMLResource(::_Module.m_hInst,sResName, sResHTML);
		CStringW s(sResHTML);

		CXFormsXFormsElement::insertResourceLocation(s,false);

		//op = causes a leak, always use attach when a BSTR already
		//	exists.
		sHTML.Attach(s.AllocSysString()); 
		if(sHTML.m_str)
			hr = S_OK;

	//WARNING:
		/*
			http://support.microsoft.com/default.aspx?scid=kb;en-us;220830
			You cannot use the Res: protocol to reference .xsl or .xml files. 
			If you try to, you may receive one of the following error messages: 
			
			"Access Denied."

			If you receive this error message, you might be making a security violation
			when you reference anything outside the current domain. Add the domain or
			protocol to the list of Trusted Sites so that the customer setting Access
			data across domains is turned on. If this does not resolve the error, 
			the only resolution is to stop using the Res: protocol.

			"No Data is Available for the Requested Resource."

			If you receive this error message, a bug is present in the Res: protocol.
			In any situation where content is downloaded synchronously by means of URLMON,
			the underlying protocol handler must create a cache file for the download to succeed.
			Unfortunately, the Res: protocol does not do this, so the whole download fails at the
			URLMON level.		
		*/

/*		CDOM3Document oDoc;
		VARIANT_BOOL bSuccess;
		
		hr = oDoc.m_pXMLParser->load(m_vtControlFile,&bSuccess);
		IXMLDOMParseError * pE = 0;
		oDoc.m_pXMLParser->get_parseError(&pE);
		if(pE)
		{
			pE->get_reason(psHTML);
			if(*psHTML)
			{
				::MessageBoxW(0,m_vtControlFile.bstrVal,L"Formsplayer failed to load User Interface Component",0);
			}
			else
			{
				hr = oDoc.m_pXMLParser->get_xml(psHTML);
			}
			::safeRelease(pE);
		}
*/	}
	else if (m_lGUIStringID && m_pImpl)
	{
		hr = S_OK;
		sHTML.LoadString(m_lGUIStringID);
	}
	return hr;
}

BSTR CXFormsElement::wrapUI(CComBSTR &sUI)
{
	// UI returned from getUI is the bare UI (probably div or span)
	//	for the control.  All controls should be wrapped in <html>
	//	tags and have MathML enabled, so this method is called by
	//	render to wrap the return value of getUI

	//load the resource string representing a prototype HTML document
	/*
	BSTR pszWrapper;
	m_pXForms->getUIWrapper(&pszWrapper);
	//insert all the bits into the right places in the string.
	wchar_t * wsVLDoc 
		=
		new wchar_t
		[
			m_pXForms->getUIWrapperLen()
			+
			wcslen(sUI)
		];
	
	swprintf(wsVLDoc,pszWrapper, sUI);

	sWrappedUI = wsVLDoc;
	delete[] wsVLDoc; 
	*/

	// BSTR pszWrapper;
	// m_pXForms->getUIWrapper(&pszWrapper);

	// BSTR pStr = sWrappedUI.Detach();

	CComBSTR pszWrapper;
	m_pXForms->getUIWrapper(&pszWrapper); 

	CComBSTR sWrappedUI(m_pXForms->getUIWrapperLen() + sUI.Length(), L"");

	swprintf(sWrappedUI.m_str, pszWrapper,  L"",  sUI);

	return sWrappedUI.Detach();
}



//The call to this (from WriteUI) could be threaded to start running before (so run concurrently with)
//	the actual writing to the viewlink document.  
//	However, the time taken by this function is fairly negligible.
void RemoveFormsPlayerInsertedCSSClasses(CComBSTR & sClassName, CComBSTR & sModifiedClassNames)
{
	if(!!sClassName)
	{
		// Bug 1142: Don't copy formsPlayer-added class names into the viewlink.
		CStringW sClassNames = sClassName.m_str;
		bool bDone = false;
		int i = 0;
		while(!bDone) {
			CStringW sClass = sClassNames.Tokenize(L" ", i);
			if(sClass.IsEmpty())
				bDone = true;
			else if(sClass != L"disabled" && sClass != L"enabled" && sClass != L"valid" && sClass != L"invalid" && sClass != L"required" && sClass != L"optional" && sClass != L"read-write" && sClass != L"read-only")
			{
				if(sClass != L"pc-disabled" && sClass != L"pc-enabled" && sClass != L"pc-valid" && sClass != L"pc-invalid" && sClass != L"pc-required" && sClass != L"pc-optional" && sClass != L"pc-read-write" && sClass != L"pc-read-only")
				{
					sModifiedClassNames += sClass;
					sModifiedClassNames += " ";
				}
			}

		}
	}
}

const UINT g_msgWriteUI = WM_APP + 7;

// Fetches and writes the UI for this control into the given document
HRESULT CXFormsElement::WriteUI(IHTMLDocument2 * pDoc2)
{
	//Do not call this with NULL
	ASSERT(pDoc2);
		// TODO: This should be replaced with an in-memory stream
	/*
		* We create a safe array, copy the HTML in, and then write
		* to the document. The data to write comes from the overloaded
		* function getUI(), on each unique control type
		*/
		
	HRESULT hr = S_OK;

		
			SAFEARRAY *sfArray = 0;
			if(m_sControlName)
				hr = m_pXForms->getUIChunk(CComBSTR(m_sControlName),&sfArray);
			else
			{
				AtlTrace("!formsPlayer.dll: m_sControlName not set.\n"); 
			}
			if(!sfArray)
			{
				CComBSTR sUI;
				hr = getUI(sUI);
				if(sUI)
				{
	sfArray = ::SafeArrayCreateVector(VT_VARIANT, 0, 1);
	VARIANT* v;
	hr = ::SafeArrayAccessData(sfArray, (LPVOID*) &v);
	v->vt = VT_BSTR;
	// Return value ******** form BSTR wrapui(sUI)
	v->bstrVal = wrapUI(sUI);
	hr = ::SafeArrayUnaccessData(sfArray);
				//	AtlTrace("i:formsPlayer.dll: m_sControlName wrapUI called for: '%s'\n", m_sControlName);
					if(m_pXForms && m_sControlName)
						m_pXForms->addUIChunk(CComBSTR(m_sControlName),sfArray);

				}
			} 
			//This method leaks like a broken freezer.
			//	the problem comes from pDoc2->close()
			if (sfArray)
			{
				// If rewriting the UIScripts then the function dispatchid's 
				// must be reset.
				resetScriptDispID();
				pDoc2->open(0,CComVariant(0),CComVariant(0),CComVariant(0),0);
				pDoc2->clear();
				hr = pDoc2->write(sfArray);
				CComPtr<IHTMLElement> pBod;
				hr = pDoc2->get_body(&pBod);

				if(SUCCEEDED(hr) && pBod)
				{
					CComBSTR sModifiedClassNames;
					CComPtr<IHTMLElement> pEl;
					GetMasterElement(&pEl);
					if(pEl)
					{
						CComBSTR sClassName;
						pEl->get_className(&sClassName);
						RemoveFormsPlayerInsertedCSSClasses(sClassName,sModifiedClassNames);
					}
					pBod->put_className(sModifiedClassNames);
					//Styling by classname has been copied to the 
					//	contained vielink document's body.  This will lead
					//	to undesired effects if padding is left on the master element;
					CComPtr<IHTMLStyle> pStyle;
					pBod->get_style(&pStyle);
					if(pStyle)
						pStyle->put_border(L"none");

				}
				hr = pDoc2->close();

			}
	
	return hr;
}

HRESULT CXFormsElement::focus()
{
	HRESULT hr = S_FALSE;
	if(m_bNewMethod && m_pControlWin) {
		//unbrand();
		VARIANT vt;
		DISPPARAMS params = {0,0,0,0};
		CComBSTR sFuncName(L"put_focus");
		hr = UIExecFunction(sFuncName,&params,&vt,m_didPutFocus);
		//hr = UIExec--Script(CComBSTR("put_focus();"));	
	} else if(m_pControl) {
		CComQIPtr<IHTMLElement2> pEl2 = m_pControl;
		if(pEl2)
			hr = pEl2->focus();
	}
	return hr;
}


void CXFormsElement::OnDeleteOfSubject()
{
	m_pXForms = 0;
	if(m_pImpl)
		m_pImpl->m_pXForms = 0;
}


HRESULT CXFormsElement::Detach()
{
	HRESULT hr = E_FAIL;
#ifndef _DEBUG
	try {
#endif
		CComPtr<IHTMLDocument> pDoc;
		hr = GetViewlinkDoc(&pDoc);
		
		//Detach can be called after document unload;
		//	this means that the pointer to m_pXForms is
		//	invalid.  
		//TODO: 
		//This should be fixed by incrementing the refcount 
		//	with every control, but this temporary solution will do
		//	for now.
		// hopefully fixed by new IDeletionListener.
		//m_pXForms = 0;
		
		if(m_pImpl) {
			CComPtr<IHTMLDocument> pDoc;
			hr = GetViewlinkDoc(&pDoc);
			if(m_pXForms && pDoc) {
				
				CComQIPtr<IHTMLDocument2> pDoc2(pDoc);
				m_pXForms->RemoveScriptLibrariesFromViewlinkDocument(pDoc2);
				
				IUnknown * pUnk = 0;
				pDoc->QueryInterface(IID_IUnknown,(void**)&pUnk);
				m_pXForms->DeregisterControlContainer(pUnk);
				::safeRelease(pUnk);
			}
			const CComBSTR doGC(L"CollectGarbage();");
			UIExecScript(doGC);
			{
				// HACK: This delete causes a crash in XFormation, hence I'm wrapping it in a try/catch.
				try {
					m_pImpl->RequestDeleteAtEarliestConvenience();
				}
				catch(...) {
				}
				m_pImpl = 0;
				m_pFormControl = 0;
			}
		}
		detachEvents();
		formControlUnInitialize();
		hr = CBinaryBehavior::Detach();
		m_pEventTargetImpl.Release();
		m_pUnkInnerEventTarget.Release();
		if(m_pEventSink)
		{
			m_pEventSink.Release();
		}
#ifndef _DEBUG
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
#endif
	return hr;
}




HRESULT CXFormsElement::put_style(BSTR sWhere, BSTR sStyle)
{
	HRESULT hr = S_FALSE;
	
	if(m_bNewMethod && m_pControlWin)
	{
		CComVariant vtWhere(sWhere), vtStyle(sStyle);
		VARIANTARG args[2];
		args[1] = vtWhere;
		args[0] = vtStyle;
		
		DISPPARAMS params = {args,0,2,0};
		VARIANT vtOut;
		CComBSTR sFuncName(L"put_style");
		hr = UIExecFunction(sFuncName,&params,&vtOut,m_didPutStyle);
/*		sScript.Format("put_style('%S','%S')",sWhere,sStyle);
		BSTR bsScript = sScript.AllocSysString();
		hr = UIExec--Script(bsScript);
		::SysFreeString(bsScript);
*/	}
	return hr;
}

//TODO: further refactoring available here by creating 
//	a function like FormatJScript(string, ...)

// Escapes a string argument for use as a JScript parameter, 
//	doubly escaping newlines and single quotes

void CXFormsElement::escapeJScriptArgument(CStringW & rsArg) const 
{
	rsArg.Replace(L"'",L"\\'");
	rsArg.Replace(L"\n",L"\\n");
	rsArg.Replace(L"\r",L"\\r");
}

//Executes a script on the UI component.
//	The principal purpose of this function is to 
//	provide a centralised location for script access
//	in order to ease debugging.  
//	Debugging JScript errors caused by erroneous calls
//	would otherwise be a bit of a fag.

HRESULT CXFormsElement::UIExecScript(BSTR bsScript)
{
	HRESULT hr = E_FAIL;
	if(m_pControlWin)
	{
		//vt is always VT_NULL, nothing can be returned from
		//	execscript.
		VARIANT vt;
		BSTR bs = ::SysAllocString(L"JScript");
		hr = m_pControlWin->execScript(bsScript,bs,&vt);
		::SysFreeString(bs);
	}
	return hr;
}

HRESULT CXFormsElement::UIExecFunction(IDispatch * pDisp, BSTR bsFunctionName,DISPPARAMS * pParams,VARIANT * pvtResult,DISPID & dispid)
{
	HRESULT hr = E_INVALIDARG;
	if(pDisp)
	{
		//Get the dispid if it has not already been specified.
		if(!dispid)
		{
			hr = pDisp->GetIDsOfNames(IID_NULL, &bsFunctionName, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
			//Attempting to call a non-existant function
			//	should only occur during the development
			//	of control scripts/components.
//				ASSERT(SUCCEEDED(hr));
			if(FAILED(hr))
				dispid = 0;
		}

		if(dispid)
		{
			hr = pDisp->Invoke
			(
				dispid,	IID_NULL,
				LOCALE_SYSTEM_DEFAULT,
				DISPATCH_METHOD,
				pParams,
				pvtResult,	0,	0
			);
		}
	}
	return hr;
}

HRESULT CXFormsElement::UIExecFunction(BSTR bsFunctionName,DISPPARAMS * pParams,VARIANT * pvtResult,DISPID & dispid)
{
	HRESULT hr = E_FAIL;
	CComPtr<IHTMLDocument> pDoc;
	hr = GetViewlinkDoc(&pDoc);
	if(pDoc)
	{
		IDispatchPtr pDisp;
		pDoc->get_Script(&pDisp);
		hr = UIExecFunction(pDisp,bsFunctionName,pParams,pvtResult,dispid);
	}
	return hr;
}

// Called to fill the HTML event handler list of names
void CXFormsElement::populateEvents(void)
{
	m_lstEventNames.push_back(&EVENT_onpropertychange);
	m_lstEventNames.push_back(&EVENT_onhelp);
	m_lstEventNames.push_back(&EVENT_onfocusout);
	m_lstEventNames.push_back(&EVENT_onfocus);
	m_lstEventNames.push_back(&EVENT_onfocusin);
	m_lstEventNames.push_back(&EVENT_onkeydown);
	m_lstEventNames.push_back(&EVENT_onkeyup);
	m_lstEventNames.push_back(&EVENT_onmouseover);
	m_lstEventNames.push_back(&EVENT_onmouseout);
}

HRESULT CXFormsElement::put_XPathExpression(const BSTR bsXPath)
{
	HRESULT hr = E_NOTIMPL;
	if(m_bNewMethod) {
		m_sDebugInfo = bsXPath;
		hr = put_XPathExpression();
		if(SUCCEEDED(hr) && m_pXForms->m_XFConfig.FormBrandingDebugInfo())
			hr = SetBrandingMouseoverAttribute(bsXPath);
	}
	return hr;
}

HRESULT CXFormsElement::put_XPathExpression()
{
	HRESULT hr = E_NOTIMPL;
	if(m_bNewMethod)
	{
		CComBSTR sDebugInfo = m_sDebugInfo;
		CComVariant vtArg(sDebugInfo);
		DISPPARAMS params = {&vtArg,0,1,0};
		VARIANT vt;
		DISPID did = 0;
		CComBSTR sFuncName(L"put_XPath");
		hr = UIExecFunction(sFuncName,&params,&vt,did);
	}	
	return hr;
}

HRESULT CXFormsElement::ResolveEnclosingBinding()
{
	HRESULT hr = S_FALSE;
	if(m_pImpl)
		hr = ResolveEnclosingBinding(&m_pImpl->m_pParentImpl);

	return hr;
}

HRESULT CXFormsElement::ResolveEnclosingBinding(CImpl ** ppImpl)
{
	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	return ResolveEnclosingBinding(pEl,ppImpl);
}

HRESULT CXFormsElement::ResolveEnclosingBinding(IHTMLElement * pElement,CImpl ** ppImpl)
{
	HRESULT hr = E_UNEXPECTED;

	//ASSERT(pElement && m_pXForms);
	if(m_pXForms)
	{
		hr = S_FALSE;
		if(m_bEnclosingBindingResolved)
		{
			if(m_pImpl)
				*ppImpl = m_pImpl->m_pParentImpl;
			else
				*ppImpl = 0;
			hr = S_OK;
		}
		else
		{
			CComPtr<IHTMLElement> pParent;
			pElement->get_parentElement(&pParent);
			if(pParent)
			{
				CImpl * pImpl = 0;
				IElementBehavior * pBehavior =0;
				CBinaryBehavior::GetNamedBehavior(g_bsAttrXFormsElementController,pParent,&pBehavior);
				
				if(pBehavior)
				{
					//Attempt to fetch Impl by casting to CXFormsElement

					CXFormsElement * pBehaviourAsXFormsElement = dynamic_cast<CXFormsElement *>(pBehavior);
					if(pBehaviourAsXFormsElement)
					{
						hr = pBehaviourAsXFormsElement->get_Impl(&pImpl);
					}
					else
						hr = m_pXForms->GetParentImpl(pBehavior,&pImpl);
					::safeRelease(pBehavior);
				}

				if(pImpl)
				{
					*ppImpl = pImpl;
				}
				else
				{
					hr = ResolveEnclosingBinding(pParent,ppImpl);
				}
			}
			else
			{
				CComPtr<IDispatch> pDisp;
				pElement->get_document(&pDisp);
				hr = m_pXForms->GetParentImpl(pDisp,ppImpl);
			}
			// Bug 465 - Only make the parent as resolved if we if one.
			if(*ppImpl)
				m_bEnclosingBindingResolved	 = true;
		}
	}

	return hr;
}

// TODO: THIS FUNCTION IS TOO LONG, SPLIT INTO LOGICAL UNITS WHEN MORE TIME
HRESULT CXFormsElement::InsertBranding(void)
{
	HRESULT hr = S_OK;
	if(m_bDocumentReady && m_pXForms) {
		if(m_pXForms->m_XFConfig.ControlBranding()) {
			CComPtr<IHTMLElement> pEl;
			bool bIsNonViewlinkWithNoLabel;
			// If the element should not be branded, a null pointer is returned.
			hr = GetElementForBranding(pEl, bIsNonViewlinkWithNoLabel);
			if(pEl) {
				if(SUCCEEDED(hr)) {
					// Set brandingimage attribute
					VARIANT v;
					v.vt = VT_BSTR;
					v.bstrVal = m_pXForms->m_XFConfig.ControlBrandingLogo();
					if(v.bstrVal) {
						hr = pEl->setAttribute(CComBSTR(L"brandingimage"), v, 0);
						::SysFreeString(v.bstrVal);
						if(SUCCEEDED(hr)) {
							if(m_pXForms->m_XFConfig.FormBrandingDebugInfo()) {
								// Set brandingmouseover attribute
								v.bstrVal = m_sDebugInfo.AllocSysString();
								if(v.bstrVal) {
									hr = pEl->setAttribute(CComBSTR(L"brandingmouseover"), v, 0);
									::SysFreeString(v.bstrVal);
								}
							}
							if(bIsNonViewlinkWithNoLabel) {
								// For non-viewlinked controls that have no label, set brandingposition
								// attribute to afterBegin of the master element.
								v.bstrVal = ::SysAllocString(L"afterBegin");
								hr = pEl->setAttribute(CComBSTR(L"brandingposition"), v, 0);
								::SysFreeString(v.bstrVal);
							}
						}
					}
				} else {
					hr = FP_E_NOMEM;
					SetupErrorInfo(hr, L"CXFormsElement::InsertBranding");
				}
			}
		}
	} else {
		hr = FP_E_DOCNREADY;
		SetupErrorInfo(hr, L"CXFormsElement::InsertBranding");
	}
	return hr;
}

HRESULT CXFormsElement::GetElementForBranding(CComPtr<IHTMLElement> &pEl, bool &bIsNonViewlinkWithNoLabel)
{
	HRESULT hr = S_OK;
	// Document must be ready before processing branding
	if(m_bDocumentReady) {
		CComPtr<IHTMLDocument> pDoc;
		bIsNonViewlinkWithNoLabel = false;
		hr = GetViewlinkDoc(&pDoc);
		if(SUCCEEDED(hr))
			// Get the label element of ViewLinked Contorls so that the 
			// branding image appears after it.
			hr = GetFirstNamedElementFromDocument(pDoc, g_lpwszTagLabel, pEl);
		else {
			// Otherwise barnd the element itself.
			CComPtr<IHTMLElement> pElMaster;
			hr = GetMasterElement(&pElMaster);
			if(pElMaster && SUCCEEDED(hr))
			{
				bIsNonViewlinkWithNoLabel = true;
				pEl = pElMaster;
			}
		}
	} else {
		hr = FP_E_DOCNREADY;
		SetupErrorInfo(hr, L"CXFormsElement::GetElementForBranding");
	}
	return hr;
}

HRESULT CXFormsElement::SetBrandingMouseoverAttribute(const wchar_t * const lpwsz)
{
	HRESULT hr = S_OK;
	if(m_bDocumentReady) {
		CComPtr<IHTMLElement> pEl;
		bool b;
		hr = GetElementForBranding(pEl, b);
		if(SUCCEEDED(hr)) {
			VARIANT v;
			v.vt = VT_BSTR;
			v.bstrVal = ::SysAllocString(lpwsz);
			if(v.bstrVal) {
				hr = pEl->setAttribute(CComBSTR(L"brandingmouseover"), v, 1);
				::SysFreeString(v.bstrVal);
			} else {
				hr = FP_E_NOMEM;
				SetupErrorInfo(hr, L"CXFormsElement::SetBrandingMouseoverAttribute");
			}
		}
	} else {
		hr = FP_E_DOCNREADY;
		SetupErrorInfo(hr, L"CXFormsElement::SetBrandingMouseoverAttribute");
	}
	return hr;
}

HRESULT CXFormsElement::GetFirstNamedElementFromDocument(IHTMLDocument *pDoc, const wchar_t * const lpwszElementName, CComPtr<IHTMLElement> &pEl)
{
	HRESULT hr = S_OK;
	if(m_bDocumentReady) {
		if(pDoc) {
			CComQIPtr<IHTMLDocument3> pDoc3 = pDoc;
			if(pDoc3) {
				CComPtr<IHTMLElementCollection> pElColl;
				BSTR s = ::SysAllocString(lpwszElementName);
				if(s) {
					hr = pDoc3->getElementsByTagName(s, &pElColl);
					::SysFreeString(s);
					if(SUCCEEDED(hr)) {
						CComVariant v;
						v.vt = VT_I4;
						v.lVal = 0;
						CComPtr<IDispatch> pDisp;
						hr = pElColl->item(v, v, &pDisp);
						if(SUCCEEDED(hr)) {
							CComQIPtr<IHTMLElement> pQI = pDisp;
							if(pQI)
								pEl = pQI;
							else {
								hr = FP_E_QIFAIL;
								SetupErrorInfo(hr, L"IHTMLElement");
							}
						}
					}
				} else {
					hr = FP_E_NOMEM;
					SetupErrorInfo(hr, L"CXFormsElement::GetFirstNamedElementFromDocument");
				}
			} else {
				hr = FP_E_QIFAIL;
				SetupErrorInfo(hr, L"IHTMLDocument3");
			}
		} else {
			hr = FP_E_BADARG;
			SetupErrorInfo(hr, L"CXFormsElement::GetFirstNamedElementFromDocument");
		}
	} else {
		hr = FP_E_DOCNREADY;
		SetupErrorInfo(hr, L"CXFormsElement::GetFirstNamedElementFromDocument");
	}
	return hr;
}

	ULONG CXFormsElement::InternalRelease( void)
	{
		ULONG l = __super::InternalRelease();
		return l;
	}

	HRESULT CXFormsElement::FinalConstruct()
	{
		
		HRESULT hr = S_OK;
		
		return hr;
	}

	void CXFormsElement::FinalRelease()
	{
		//Aggregates:
		//	These are ties to the refcount of this.
		//	Therefore, in the case of finalRelease, 
		//	ensure that the pointers are 0 so that 
		//	they don't get overReleased.
		m_pEventTargetImpl.p = 0;
	}

	HRESULT CXFormsElement::getEventTargetImpl(IEventTarget ** ppTargetImpl)
	{

		HRESULT hr = E_NOINTERFACE;
		if(!m_pEventTargetImpl)
		{
			CComPtr<IHTMLElement> pEl;
			GetMasterElement(&pEl);
			CComPtr<IUnknown> pUnk = pEl;

			hr =  CoCreateInstance
			(
				CLSID_EventTarget, 
				pUnk, 
				CLSCTX_INPROC, 
				__uuidof(IUnknown), 
				reinterpret_cast<void**>(&m_pUnkInnerEventTarget)
			);

			if(m_pUnkInnerEventTarget)
			{
				hr = m_pUnkInnerEventTarget->QueryInterface(IID_IEventTarget,reinterpret_cast<void**>(&m_pEventTargetImpl));
				//TODO: I don't think we need this as a member - if this works, make it local.
			//	m_pUnkInnerEventTarget.Release();
			}
			else
				ASSERT(0);
		}
		
			
		if(ppTargetImpl)
		{
			if(m_pEventTargetImpl)
				hr = m_pEventTargetImpl.CopyTo(ppTargetImpl);
		}
		else
		{
			hr = E_POINTER;
		}

		return hr;
	}


	
STDMETHODIMP CXFormsElement::get_formKey(LONG* pVal)
{
	HRESULT hr = S_FALSE;
	if(m_lFormKey)
	{
		*pVal = m_lFormKey; 
		hr = S_OK;
	}
	return hr;
}

STDMETHODIMP CXFormsElement::put_formKey(LONG newVal)
{
	HRESULT hr = E_ACCESSDENIED;
	if(!m_pXForms)
	{
		m_lFormKey = newVal; 
		hr = S_OK;
	}

	return hr;
}


HRESULT CXFormsElement::addXblParentNode(IHTMLDocument * pDoc, IHTMLElement * pMaster)
{
	HRESULT hr = S_OK;
	
    // Add the xblParentNode property to the viewlinks document which 
	// points to the master element.
	CComQIPtr<IDispatchEx> pDocDisp = pDoc;
	if(pDocDisp) {
		CComQIPtr<IDispatch> pMstDisp = pMaster;
		if(pMstDisp) {
			VARIANT vtMasterEl;
			vtMasterEl.vt = VT_DISPATCH;
			pMstDisp->QueryInterface(&vtMasterEl.pdispVal);
			// If I could get DISPATCH_PROPERTYPUTREF to work I could just use
			// this code below.
			// vtMasterEl.pdispVal = pMstDisp;

			// Create the property xblParentNode and get the dispid for it
			DISPID lDispID;
			hr = pDocDisp->GetDispID(CComBSTR(L"xblParentNode"), fdexNameEnsure, &lDispID);
			if(SUCCEEDED(hr)) {
				DISPID lPutID = DISPID_PROPERTYPUT;
				DISPPARAMS dispparams;
				dispparams.rgvarg = &vtMasterEl;
				dispparams.rgdispidNamedArgs = &lPutID;
				dispparams.cArgs = 1;
				dispparams.cNamedArgs = 1;
				// Set the value of the attribute xblParentNode using the dispid
				hr = pDocDisp->InvokeEx(lDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
			}
		}
	}
	return hr;
}


HRESULT CXFormsElement::addHostDocumentProperty(IHTMLDocument * pDoc, IDispatch * pHostDocument)
{
	HRESULT hr = S_OK;
	
    // Add the xblParentNode property to the viewlinks document which 
	// points to the master element.
	CComQIPtr<IDispatchEx> pDocDisp = pDoc;
	if(pDocDisp) {
		CComQIPtr<IDispatch> pMstDisp = pHostDocument;
		if(pMstDisp) {
			VARIANT vtHostDoc;
			vtHostDoc.vt = VT_DISPATCH;
			pHostDocument->QueryInterface(&vtHostDoc.pdispVal);
			// If I could get DISPATCH_PROPERTYPUTREF to work I could just use
			// this code below.
			// vtMasterEl.pdispVal = pMstDisp;

			// Create the property xblParentNode and get the dispid for it
			DISPID lDispID;
			hr = pDocDisp->GetDispID(CComBSTR(L"hostDocument"), fdexNameEnsure, &lDispID);
			if(SUCCEEDED(hr)) {
				DISPID lPutID = DISPID_PROPERTYPUT;
				DISPPARAMS dispparams;
				dispparams.rgvarg = &vtHostDoc;
				dispparams.rgdispidNamedArgs = &lPutID;
				dispparams.cArgs = 1;
				dispparams.cNamedArgs = 1;
				// Set the value of the attribute xblParentNode using the dispid
				hr = pDocDisp->InvokeEx(lDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
			}
		}
	}
	return hr;
}

long CXFormsElement::getParentOrdinal()
{
	long l = -1;
	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	if(pEl)
	{
		HRESULT hr;
		CComPtr<IHTMLElement> pParent;
		pEl->get_parentElement(&pParent);
		while(pParent)
		{
			CComBSTR bsTag;
			pParent->get_tagName(&bsTag);
			if(bsTag == L"repeat")
			{
				break;
			}
			else
			{
				CComVariant vtVal;
				hr = pParent->getAttribute(L"parentOrdinal",1,&vtVal);
				if(hr == S_OK && vtVal.vt == VT_UI4)
				{
					l = static_cast<long>(vtVal.uiVal);
					pParent.Release();
					break;
				}
				else
				{
					CComPtr<IHTMLElement> pGrandParent;
					pParent->get_parentElement(&pGrandParent);
					pParent.Release();
					pParent = pGrandParent;
				}
			}
		}
	}
	return l;
}

HRESULT CXFormsElement::put_MIPProxyElement(IHTMLElement * pEl)
{
	m_pMIPProxy = pEl;
	bool bModified;
	SetClassPropertyOnElement(m_pMIPProxy, m_bEnabled,  m_bReadonly,  m_bValid,  m_bRequired, &bModified);
	return S_OK;
}

HRESULT CXFormsElement::get_ModelId(BSTR * psModel)
{
	CComPtr<IHTMLElement> pEl;
	HRESULT hr = GetMasterElement(&pEl);
	if(pEl)
	{
		CComVariant vt;
		hr = pEl->getAttribute(ATTR_MODEL,1,&vt);
		if(vt.vt == VT_BSTR)
		{
			CComBSTR bs = vt.bstrVal;
			*psModel = bs.Detach();
			hr = S_OK;
		}
		else
		{
			hr = S_FALSE;
		}
	}
	return hr;
}
const CComBSTR prop_currentValue("currentValue");
const CComBSTR prop_label("label");
const CComBSTR prop_hint("hint");
const CComBSTR prop_help("help");
const CComBSTR prop_alert("alert");


STDMETHODIMP CXFormsElement::SetProperty(BSTR sProperty, BSTR sValue)
{
	HRESULT hr = E_NOTIMPL;
	if(prop_currentValue == sProperty)
	{
		if(m_bEnabled)
		{
			if(m_pFormControl)
 				hr = m_pFormControl->putValue(sValue);
		}
		else
			hr = putDisabledValue();
	}
	return hr;
}

STDMETHODIMP CXFormsElement::GetProperty(BSTR sProperty, BSTR * sValue)
{
	HRESULT hr = E_NOTIMPL;
	if(prop_currentValue == sProperty)
	{
		hr = get_CurrentValue(sValue);
	}
	//TODO: fill in the gaps below.
	else if(prop_label == sProperty)
	{
	
	}
	else if(prop_hint == sProperty)
	{
	
	}
	else if(prop_alert == sProperty)
	{
	
	}
	else if(prop_help == sProperty)
	{
	
	}
	return hr;
}

STDMETHODIMP CXFormsElement::get_CurrentValue(BSTR *psCurrentValue)
{
	HRESULT hr = S_OK;

	
	if(psCurrentValue)
	{
		if(!!m_sCurrentValue)
			*psCurrentValue = m_sCurrentValue.Copy();
		else if(m_pImpl && !m_pImpl->m_bisBound)
		{
			CComPtr<IHTMLElement> pEl;
			GetMasterElement(&pEl);
			if(pEl)
				pEl->get_innerText(psCurrentValue);
		}

		else
			hr = S_FALSE;
	}
	else
		hr = E_POINTER;

	return hr;
}



HRESULT CXFormsElement::HTMLContains(IHTMLElement * pEl,bool * pbResult) const
{
	HRESULT hr;
	if(pbResult)
	{
		CComPtr<IHTMLElement> pMaster;
		hr = GetMasterElement(&pMaster);
		if(pMaster)
		{
			VARIANT_BOOL vtb;
			hr =pMaster->contains(pEl,&vtb);
			if(SUCCEEDED(hr))
			{
				*pbResult = (vtb==VARIANT_TRUE);
			}
		}
	}
	else
		hr = E_POINTER;
	return hr;
}

